
/*
 *  Copyright 2007 Marc Lanctot and Marc Gendron-Bellemare
 *
 *  This file is part of OpenMTG.
 *
 *  OpenMTG is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  OpenMTG is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with OpenMTG.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

import java.util.*; 

public class Player
{
  private int id; 
  private String name; 
  private int lifePoints; 
  private ArrayList<Card> hand; 
  protected Deck deck;
  
  // these are only kept on the client side
  protected int libsize; 
  protected int handSize;

  protected String manaPool;

  // permanents
  protected ArrayList<Card> lands;
  protected ArrayList<Card> creatures;
  protected ArrayList<Card> artifacts;
  protected ArrayList<Card> enchantments;

	protected LinkedList<Card> aGraveyard;
	
	protected DiscardInfo aDiscard;

	// stashed in here when removed before put elsewhere 
	// (graveyard or out of the game)
	protected ArrayList<Card> removedCards;

  public Player(int id, String name)
  {
    this.id = id; 
    this.name = name;
    this.deck = null;
    this.hand = new ArrayList<Card>();
    lifePoints = 20;
    handSize = 0; 

    lands = new ArrayList<Card>(); 
    creatures = new ArrayList<Card>(); 
    artifacts = new ArrayList<Card>(); 
    enchantments = new ArrayList<Card>(); 
		aGraveyard = new LinkedList<Card>();
		removedCards = new ArrayList<Card>();
		

    manaPool = "";
		aDiscard = new DiscardInfo();
  }

  public void addLifePoints(int lp) {
    lifePoints += lp; 
  }
  
  public void setDeck(Deck deck) {
    this.deck = deck;
  }

  public void setLifePoints(int points) {
    this.lifePoints = points;
  }

  public boolean ready() {
    return (deck != null);
  }

  public String getInfo() {
    return (id + " " + name + " " + lifePoints + " " + hand.size() + " " + deck.size());
  }

  public Deck getDeck() {
    return deck;
  }

  public Card draw()
  {
    Card c = deck.deal(); 
    hand.add(c);
    handSize++;
    return c;
  }

  public void draw(int num)
  {
    for (int i = 0; i < num; i++)
      draw();
  }

  public ArrayList<Card> getHand() {
    return hand;
  }

  public String getName() { return name; }

  public int getLifePoints() { return lifePoints; }

  public int getId() { return id; }

  public int getHandSize() { return handSize; }

	public int getMaxHandSize() { return 7; } // @@@ fixme

  public void setHandSize(int handSize) 
  { this.handSize = handSize; } 

  public boolean hasAllLand() {
    for (Card c : hand)
      if (!c.isBasicLand())
        return false;

    return true;
  }

  public boolean hasNoLand() { 
    for (Card c : hand) 
      if (c.isBasicLand())
        return false; 

    return true;
  }

  public void clearHand() {
    handSize = 0;
    hand.clear(); 
  }

  public Card getCardInHand(int iid) {
    Card card = null;

    for (Card c : hand)
      if (c.getIID() == iid)
        card = c;

    return card;
  }

  public boolean removeCardFromHand(Card c)
  {
    if (!hand.remove(c))
      return false;

    handSize--;
    return true;
  }


  public boolean playLand(Card land) {

    if (!removeCardFromHand(land))
      return false;

    lands.add(land); 

    return true;
  }

  public Card getLand(int iid) {
    for (Card c : lands)
      if (c.getIID() == iid)
        return c;

		return null;
  }

	public Card getCreature(int iid) {
		for (Card c : creatures)
			if (c.getIID() == iid)
				return c;
		
		return null;
	}
	
	public Card getArtifact(int iid) {
		for (Card c : artifacts)
			if (c.getIID() == iid)
				return c;
		
		return null;
	}
	
	public Card getEnchantment(int iid) {
		for (Card c : enchantments)
			if (c.getIID() == iid)
				return c;
		
		return null;
	}
	
	public ArrayList<Card> getLands() { return lands; }
	public ArrayList<Card> getCreatures() { return creatures; }
	public ArrayList<Card> getArtifacts() { return artifacts; }
	public ArrayList<Card> getEnchantments() { return enchantments; }

  public void addMana(char mana)
  {
    manaPool += mana;
  }

  // Check for mana (assumes spell casting from hand)
  public boolean hasManaFor(int iid)
  {
    Card c = getCardInHand(iid); 
    String mpcopy = new String(manaPool);

    for (int i = 0; i < c.cost.length(); i++) {
      char costChar = c.cost.charAt(i); 
      String oldcopy = new String(mpcopy);
      mpcopy = mpcopy.replaceFirst("" + costChar, "#");

      if (oldcopy.equals(mpcopy))
        return false;
    }

    return true;
  }

  public void removeManaFor(int iid)
  {
    Card c = getCardInHand(iid); 
    removeManaFor(c);
  }

  public void removeManaFor(Card c) 
  {
    String mpcopy = new String(manaPool);

    for (int i = 0; i < c.cost.length(); i++) {
      char costChar = c.cost.charAt(i); 
      String oldcopy = new String(mpcopy);
      mpcopy = mpcopy.replaceFirst("" + costChar, "#");
    }

    mpcopy = mpcopy.replaceAll("#", " "); 
    manaPool = mpcopy;
  }

  public Card getCardInCreatures(int iid) {
    Card card = null;

    for (Card c : creatures)
      if (c.getIID() == iid)
        card = c;

    return card;
  }

	public boolean removeFromCreatures(Card pDead, ArrayList<Message> pResponses)
	{
		boolean wasThere = creatures.remove(pDead);

		if (pResponses != null)
		{
			pResponses.add (new Message(-1,
				"REMOVE "+getId()+" "+pDead.getIID()));
			removedCards.add(pDead);
		}
		
		return wasThere;
	}
	
	public void addToGraveyard(Card c, ArrayList<Message> pResponses)
	{
		System.err.println (c +" to graveyard");
		aGraveyard.addFirst(c);
		removedCards.remove(c); 

		if (pResponses != null)
		{
			pResponses.add (new Message(-1, "GRAVEYARD "+getId()+" "+c.getIID()));
		}
	}
	
	public boolean mustDiscard() { return aDiscard.mustDiscard(); }

	public int getNumCardsDiscard() { return aDiscard.getNumCardsDiscard(); }

	public void discard(Card c, ArrayList<Message> pResponses)
	{
		// Remove, put in graveyard, notify the discard system
		removeCardFromHand(c);
		addToGraveyard(c, pResponses);
		aDiscard.discard();
	}

	public void fakeDiscard() { aDiscard.discard(); }
	
	public void addToDiscard(int pNum) 
	{ 
		aDiscard.addNumCardsDiscard(pNum); 
	}
	
	/* Returns true if at least one creature can attack */
	public boolean canAttack()
	{
	  for (Card c : creatures)
	  {
	    if (c.canAttack())
	      return true; 
	  }
	  
	  return false; 
	}
	
	/* Counts untapped land and mana pool */ 
	public boolean canCast(int iid) 
	{
	  if (hasManaFor(iid))
	    return true; 
	  
    Card c = getCardInHand(iid);     
    
    String manaAvailable = new String(manaPool);
    
    // go through untapped land and add that to the available mana
    for (Card l : lands)
    {
      if (l.isUntapped() && l.isBasicLand())
        manaAvailable += l.producesMana(); 
    }

    for (int i = 0; i < c.cost.length(); i++) {
      char costChar = c.cost.charAt(i); 
      String oldcopy = new String(manaAvailable);
      manaAvailable = manaAvailable.replaceFirst("" + costChar, "#");

      if (oldcopy.equals(manaAvailable))
        return false;
    }

    return true;
    
	}
	
	public ArrayList<Integer> findLandsToCast(int iid)
	{
    Card c = getCardInHand(iid);     
    ArrayList<Integer> list = new ArrayList<Integer>(); 
    
    String cost = c.getCost(); 
    
    for (int i = cost.length()-1; i >= 0; i--)
    {
      String manaNeeded = "" + cost.charAt(i);
      boolean foundManaNeeded = false; 
      
      for (Card l : lands)
      {
        if (l.isUntapped() && l.isBasicLand())
        {
          // FIXME when support costs of > 10
          if (!Character.isDigit(manaNeeded.charAt(0)))
          {
            if (l.producesMana().equals(manaNeeded) && !list.contains(l.getIID()))
            {
              list.add(l.getIID());
              foundManaNeeded = true;
              break; 
            }
          }
          else if (!list.contains(l.getIID()))
          {
            list.add(l.getIID());
            foundManaNeeded = true;
            break;               
          }
        }
      }
      
      if (!foundManaNeeded)
        return null; // failed
    }

    return list;     
	}

	public Card getCardInRemoved(int iid) {
	  Card card = null;
	  
	  for (Card c : removedCards)
	  if (c.getIID() == iid)
	  card = c;
	  
	  return card;
	}

}
